/* eslint-disable react/no-children-prop */
import React from 'react';
import { Controller, useForm } from 'react-hook-form';
import MaskedInput from 'react-text-mask';

import { ReactComponent as Check } from '@/assets/images/auth/Check.svg';
import { ReactComponent as ChevronRight } from '@/assets/images/auth/ChevronRight.svg';
import { getIsAuth, getUserId } from '@/entities/auth/model/selectors';
import { Avatar, SearchInput } from '@/entities/profile';
import { clearCities, editProfile, getCities, getProfile, setPagination } from '@/entities/profile/model/actions';
import { getCitiesList, getEditProfileSuccess, getUserProfile } from '@/entities/profile/model/selectors';
import { FormBlock } from '@/entities/profile/ui/FormBlock';
import { sorts, sortTitleEnglish, sortTitleRu } from '@/shared/constants/objects';
import { regex } from '@/shared/constants/regex';
import { ModalContext } from '@/shared/context/ModalContext';
import { Modals, SelectProps } from '@/shared/interfaces';
import { CityDto } from '@/shared/interfaces/generated';
import { normalizeData } from '@/shared/lib';
import { useMetaTag, usePreventRedirect, useScrollLock } from '@/shared/lib/hooks';
import { formatDate, insertSpaces } from '@/shared/lib/validators';
import { useAppDispatch, useAppSelector } from '@/shared/store';
import { Button, ButtonVariants, FlexWrapper, Input, Modal, Select, Snackbar, Title } from '@/shared/ui';
import { yupResolver } from '@hookform/resolvers/yup';
import dayjs from 'dayjs';
import { Router, useRouter } from 'next/router';
import { useSnackbar } from 'notistack';
import { useWindowSize } from 'usehooks-ts';
import * as yup from 'yup';

import cs from './Main.module.scss';

const schema = yup.object().shape({
  city: yup.string(),
  name: yup.string(),
  phone: yup.string().matches(regex.phone),
  dateOfBirth: yup.string().matches(/\b(0[1-9]|[12][0-9]|3[01])\.(0[1-9]|1[0-2])\.\d{4}\b/),
});

export const Main = () => {
  const profile = useAppSelector(getUserProfile);
  const isAuth = useAppSelector(getIsAuth);
  const cities = useAppSelector(getCitiesList);
  const userId = useAppSelector(getUserId);
  const editSuccess = useAppSelector(getEditProfileSuccess);

  const [redirectUrl, setRedirectUrl] = React.useState<string | null>(null);
  const [isChoosing, setIsChoosing] = React.useState(false);
  const [exitting, setExitting] = React.useState<boolean>(false);
  const [isConfirmOpen, setIsConfirmOpen] = React.useState<boolean>(false);
  const [city, setCity] = React.useState<CityDto | null>(profile?.city as CityDto);
  const [sort, setSort] = React.useState<SelectProps | undefined>(
    profile.familyRole
      ? {
          id: sortTitleEnglish[profile.familyRole as string],
          title: sortTitleRu[profile.familyRole as string],
        }
      : undefined,
  );
  const [show, setShow] = React.useState<{ date: boolean; phone: boolean; city: boolean }>({
    phone: false,
    date: false,
    city: false,
  });

  const dispatch = useAppDispatch();
  const router = useRouter();
  const { enqueueSnackbar } = useSnackbar();
  const { openModal, setData } = React.useContext(ModalContext);
  const { lock, unlock } = useScrollLock();
  const { width } = useWindowSize();

  const handleCloseCityModal = () => {
    setShow((prevState) => ({ ...prevState, city: false }));

    // Снимаем фокус со всех возможных элементов
    const activeElement = document.activeElement as HTMLElement;
    if (activeElement && typeof activeElement.blur === 'function') {
      activeElement.blur();
    }
  };

  React.useLayoutEffect(() => {
    (show.city && width < 768) || isConfirmOpen || (isChoosing && width < 768) ? lock() : unlock();
  }, [show.city, isConfirmOpen, isChoosing]);

  const {
    formState: { isDirty },
    setValue,
    control,
    watch,
    getValues,
    reset,
  } = useForm({
    reValidateMode: 'onChange',
    defaultValues: {
      city: profile.city?.name ?? '',
      name: profile.name ?? '',
      phone: insertSpaces(profile.phoneNumber as string) ?? '',
      dateOfBirth: dayjs(profile?.dateOfBirth).format('DD.MM.YYY') ?? '',
    },
    resolver: yupResolver(schema),
  });

  React.useEffect(() => {
    setValue('city', profile.city?.name ?? watch('city'));
    setValue('name', profile.name ?? watch('name'));
    setValue('phone', insertSpaces(profile.phoneNumber as string) ?? watch('phone'));
    setValue('dateOfBirth', dayjs(profile?.dateOfBirth).format('DD.MM.YYYY') ?? watch('dateOfBirth'));
    setCity(profile.city as CityDto);
    setSort({
      id: sortTitleEnglish[profile.familyRole as string],
      title: sortTitleRu[profile.familyRole as string],
    });
  }, [profile]);

  const handleSortChange = React.useCallback((sort: SelectProps) => {
    setSort(sort);
  }, []);

  const onSelectClick = (option: { label: string; value: number }) => {
    setValue('city', option.label, { shouldDirty: option.label !== profile.city?.name });
    setCity({
      id: option.value,
      name: option.label,
    });
    setShow((prevState) => ({ ...prevState, city: false }));
  };

  React.useEffect(() => {
    Router.events.on('routeChangeStart', setRedirectUrl);
    return () => {
      Router.events.off('routeChangeStart', setRedirectUrl);
    };
  }, [isConfirmOpen]);

  React.useEffect(() => {
    !isAuth ? setExitting(true) : setExitting(false);
  }, [isAuth]);

  const handleExit = () => {
    setExitting(true);
    if (redirectUrl) {
      router.push(redirectUrl);
    }
    setIsConfirmOpen(false);
  };

  const handleCreateObjectToSubmit = () => ({
    familyRole: sortTitleEnglish[sort?.title as string],
    name: watch('name').trim() ? watch('name') : null,
    cityId: cities.data.find((item) => item.name === watch('city'))?.id || null,
    dateOfBirth: watch('dateOfBirth') ? formatDate(watch('dateOfBirth')) : null,
    phoneNumber: watch('phone').length > 0 ? watch('phone').trim().replace(/\s/g, '') : null,
    avatar: profile?.avatar || '',
  });

  const handleOpenAvatarModal = async () => {
    setData(() => handleCreateObjectToSubmit());
    openModal(Modals.EditAvatar);
  };

  const editProfileHandler = async (submit?: boolean) => {
    reset({ ...getValues() }, { keepIsValid: true });
    dispatch(clearCities());
    try {
      await dispatch(
        editProfile({
          id: userId as number,
          profile: handleCreateObjectToSubmit(),
        }),
      ).unwrap();

      setIsConfirmOpen(false);
      const successMessage = submit ? 'Данные сохранены' : 'Данные сохранены';
      enqueueSnackbar(successMessage, {
        variant: 'success',
        content: (key, message) => <Snackbar id={key} message={message} size="md" variant="success" />,
      });
      if (!submit && redirectUrl) {
        await router.push(redirectUrl);
        dispatch(getProfile(userId as number));
      }
    } catch (error) {
      enqueueSnackbar('Произошла ошибка', {
        variant: 'error',
        content: (key, message) => <Snackbar id={key} message={message} size="md" variant="error" />,
      });
      setIsConfirmOpen(false);
    }
  };
  React.useEffect(() => {
    const debounceTimeout = setTimeout(() => {
      dispatch(getCities({ search: watch('city') || '', offset: cities.offset }));
    }, 500);
    return () => clearTimeout(debounceTimeout);
  }, [watch('city'), editSuccess]);

  const isPhoneValid = watch('phone').length !== 0 && watch('phone').length < 16;
  const isDateOfBirthValid = watch('dateOfBirth').length === 10 && !dayjs(formatDate(watch('dateOfBirth'))).isValid();
  const changedSortCondition = !!(sort && sort.title !== sortTitleRu[profile.familyRole as string]);
  const disabledCondition = (!isDirty && !changedSortCondition) || isPhoneValid || isDateOfBirthValid;

  useMetaTag((isChoosing || isConfirmOpen) && width < 1024, '#999999');
  usePreventRedirect((isDirty || changedSortCondition) && !isConfirmOpen && !exitting && (isAuth as boolean), () =>
    setIsConfirmOpen(true),
  );

  return (
    <>
      <Avatar handleOpenAvatarModal={handleOpenAvatarModal} isProfile />
      <form className={cs.form}>
        <FormBlock htmlFor="name" name="Имя">
          <Controller
            control={control}
            name="name"
            render={({ field }) => <Input {...field} data-testid="input-name" id="name" />}
          />
        </FormBlock>
        {width > 1024 && (
          <FormBlock htmlFor="role" name="Роль">
            <Select
              className={cs.sort}
              config={{
                title: sort?.title || '',
                values: sorts,
              }}
              isBig
              isFullWidth
              isHover={false}
              onSelect={handleSortChange}
              selected={sorts.filter((item) => item.title === sort?.title)}
              withPlaceholder={sort?.title ? sort?.title : 'Выберите'}
            />
          </FormBlock>
        )}
        {width <= 1024 && (
          <FlexWrapper className={cs.formBlock}>
            <FormBlock htmlFor="role" name="Роль">
              <div style={{ position: 'relative', zIndex: '1' }}>
                <Input
                  data-testid="input-role"
                  disabled
                  onClick={() => {
                    setIsChoosing(true);
                  }}
                  placeholder="Выберите"
                  value={sort?.title}
                />
                <div
                  className={cs.chevronLeft}
                  onClick={() => {
                    setIsChoosing(true);
                  }}
                >
                  <ChevronRight />
                </div>
              </div>
            </FormBlock>
          </FlexWrapper>
        )}
        <FormBlock htmlFor="dateOfBirth" name="Дата рождения">
          <Controller
            control={control}
            name="dateOfBirth"
            render={({ field }) => (
              <MaskedInput
                className={cs.input}
                guide={false}
                id="dateOfBirth"
                {...field}
                mask={[/\d/, /\d/, '.', /\d/, /\d/, '.', /\d/, /\d/, /\d/, /\d/]}
                onBlur={() => setShow((prevState) => ({ ...prevState, date: false }))}
                onFocus={() => setShow((prevState) => ({ ...prevState, date: true }))}
                placeholder="ДД.ММ.ГГГГ"
                type="numeric"
              />
            )}
          />
        </FormBlock>
        <FormBlock htmlFor="city" name="Город">
          <Controller
            control={control}
            name="city"
            render={({ field }) => (
              <SearchInput
                {...field}
                activeOption={city}
                autoFocus={width < 1024 && show.city && watch('city').length === 0}
                disabled={width < 1024 && !show.city}
                id="city"
                isAnotherInput={width < 1024}
                isClose={watch('city').length > 0}
                isOpen={show.city}
                isSelect
                onBlur={() =>
                  width > 1024 &&
                  setTimeout(() => show.city && setShow((prevState) => ({ ...prevState, city: false })), 250)
                }
                onClear={() => {
                  setValue('city', '', { shouldDirty: true, shouldTouch: true });
                  const inputElement = document.querySelector(`input[aria-label=${'another_input'}]`);
                  inputElement && (inputElement as HTMLElement).focus();
                }}
                onClick={() => setShow((prevState) => ({ ...prevState, city: true }))}
                onClose={handleCloseCityModal}
                onKeyDown={(e) => {
                  if (e.key === 'Enter' && width < 1024) {
                    setTimeout(() => setShow((prevState) => ({ ...prevState, city: false })), 250);
                  }
                }}
                onSelectClick={onSelectClick}
                options={normalizeData(cities?.data, 'id', 'name')}
                pagination={
                  cities.data.length > 19
                    ? {
                        offset: cities.offset,
                        timeout: cities.isLoading,
                        callback: async () => {
                          dispatch(setPagination());
                          await dispatch(getCities({ search: watch('city') || '', offset: cities.offset }));
                        },
                      }
                    : null
                }
                placeholder="Выберите"
                text="Если города нет в списке, введите вручную"
                title="Город"
              />
            )}
          />
          {width < 1024 && (
            <div
              className={cs.chevronLeft}
              onClick={() => width < 1024 && setShow((prevState) => ({ ...prevState, city: true }))}
            ></div>
          )}
        </FormBlock>
        <FormBlock htmlFor="phone" name="Телефон">
          <Controller
            control={control}
            name="phone"
            render={({ field }) => (
              <MaskedInput
                {...field}
                className={cs.input}
                guide={false}
                id="phone"
                mask={['+', '7', ' ', /[1-9]/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, ' ', /\d/, /\d/, ' ', /\d/, /\d/]}
                onBlur={() => setShow((prevState) => ({ ...prevState, phone: false }))}
                onFocus={() => setShow((prevState) => ({ ...prevState, phone: true }))}
                placeholder={show.phone ? '+7' : ''}
                type="numeric"
              />
            )}
          />
        </FormBlock>
      </form>
      <Button
        aria-label="profile-submit"
        className={cs.buttons}
        data-testid="button-edit_profile"
        disabled={disabledCondition}
        onClick={() => editProfileHandler(true)}
        type="submit"
        variant={ButtonVariants.Primary}
      >
        Сохранить
      </Button>
      <Modal className={cs.modal} isOpen={isConfirmOpen} onCloseClick={() => setIsConfirmOpen(false)} zIndex={2000}>
        <FlexWrapper gap={width > 1024 ? 32 : 24} isCenter>
          <Title size="modal-size">Сохранить изменения?</Title>
          <Button
            children="Сохранить"
            disabled={false}
            onClick={() => editProfileHandler(false)}
            variant={ButtonVariants.Primary}
          />
          <Button onClick={handleExit} variant={ButtonVariants.Logout}>
            Не сохранять
          </Button>
        </FlexWrapper>
      </Modal>
      <Modal className={cs.modal} isMobile isOpen={isChoosing} onCloseClick={() => setIsChoosing(false)}>
        <Title className={cs.title} size="modal-size">
          Роль
        </Title>
        <div className={cs.chooseCont}>
          {sorts.map((item, index) => (
            <span
              className={index !== sorts.length - 1 ? cs.chooseItem : cs.chooseItemWithoutBorder}
              key={item?.id}
              onClick={() => {
                setSort(item);
                setTimeout(() => {
                  setIsChoosing(false);
                }, 50);
              }}
            >
              <>
                {item.title}
                {item.title === sort?.title && (
                  <div className={cs.check}>
                    <Check />
                  </div>
                )}
              </>
            </span>
          ))}
        </div>
      </Modal>
    </>
  );
};
